﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using Framework.Data.Core;
using Framework.Data.Exceptions;
public class ChildrenContainer : IEntityCollection<object, IBaseEntity>, IContainer
{

    #region "Membres"
    //Type des entités du container
    protected System.Type _entitiesType;
    //Filtre sur la collection
    protected List<string> _filters = new List<string>();
    //Le parent
    protected IBaseEntity _parent = null;
    //Nom du champ qui représente la clé primaire du parent dans la table enfant
    protected string _fieldNameToLink = string.Empty;
    //Nom du champ qui représente la description du parent dans l'object enfant
    protected string _descriptionFieldNameToLink = string.Empty;
    //Liste des enfants
    protected IEntityCollection<object, IBaseEntity> _entityCollection;
    //Indique si la collection à déjà été loader

    protected bool _isLoaded = false;
    #endregion

    #region "Constructeurs"

    /// <summary>
    /// Constructeur
    /// </summary>
    /// <param name="entitiesType">Types des entités</param>
    /// <param name="parent">L'entité parent</param>
    /// <param name="fieldNameToLink">Nom du champ qui représente la clé primaire du parent dans la table enfant</param>
    /// <param name="descriptionFieldNameToLink">La description du parent qui va servir à peuplé le foreign de l'enfant vers son parent</param>
    /// <remarks></remarks>
    public ChildrenContainer(System.Type entitiesType, IBaseEntity parent, string fieldNameToLink, string descriptionFieldNameToLink)
    {
        this._entitiesType = entitiesType;
        this._entityCollection = new EntityCollection<object, IBaseEntity>(EntityType);
        this._parent = parent;
        this._fieldNameToLink = fieldNameToLink;
        this._descriptionFieldNameToLink = descriptionFieldNameToLink;
    }
    #endregion

    #region "Propriétés"

    public System.Type EntitiesType
    {
        get { return this._entitiesType; }
        set { this._entitiesType = value; }
    }

    public List<string> Filters
    {
        get { return this._filters; }
    }

    public IBaseEntity Parent
    {
        get { return this._parent; }
        set { this._parent = value; }
    }

    public string FieldNameToLink
    {
        get { return this._fieldNameToLink; }
        set { this._fieldNameToLink = value; }
    }

    #endregion

    #region "Méthodes"

    /// <summary>
    /// Procédure qui set le parent à l'enfant
    /// </summary>
    /// <param name="entity">L'enfant pour lequel nous voulons associer son parent</param>
    /// <remarks></remarks>
    protected void SetParent(IBaseEntity entity)
    {
        IEntityField parentField = (IEntityField)entity.GetField(this._fieldNameToLink);
        parentField.Value = this.Parent.GetPrimaryKeyField().Value;
        if (this._descriptionFieldNameToLink != null)
        {
            IForeignField parentDescriptionField = (IForeignField)entity.GetField(this._descriptionFieldNameToLink);
            parentDescriptionField.Value = this.Parent.GetField(parentDescriptionField.DatabaseFieldName).Value;
        }
    }

    #endregion

    #region "IEntityCollection"

    /// <summary>
    /// Fonction qui permet de convertir la collection en Liste d'objet
    /// </summary>
    /// <param name="displayFieldName">Le champ à utiliser</param>
    /// <returns>ListObject()</returns>
    /// <remarks></remarks>
    public ListObject[] ToListObject(string displayFieldName)
    {
        return this._entityCollection.ToListObject(displayFieldName);
    }

    /// <summary>
    /// Fonction qui permet de convertir la collection en Liste d'objet
    /// </summary>
    /// <param name="displayFieldName">Le champ à utiliser</param>
    /// <param name="sortList">Indique si la liste doit être trié par le DisplayField</param>
    /// <returns>ListObject()</returns>
    /// <remarks></remarks>
    public ListObject[] ToListObject(string displayFieldName, bool sortList)
    {
        return this._entityCollection.ToListObject(displayFieldName, sortList);
    }

    /// <summary>
    /// Fonction qui converti la collection en IList
    /// </summary>
    /// <param name="displayFieldsName">Champs qui sera affiché séparé par une virgule. Ex: "Nom, Description"</param>
    /// <param name="seperator">Chaîne utilisé pour séparé les champs lors de l'affichage</param>
    /// <param name="sortList">Indique si la liste doit être triée</param>
    /// <returns>IList</returns>
    /// <remarks></remarks>
    public ListObject[] ToListObject(string displayFieldsName, string seperator, bool sortList)
    {
        return this._entityCollection.ToListObject(displayFieldsName, seperator, sortList);
    }

    /// <summary>
    /// Procédure d'ajout d'un entité dans la collection
    /// </summary>
    /// <param name="item">Item à ajouter</param>
    /// <remarks></remarks>
    public void Add(System.Collections.Generic.KeyValuePair<object, IBaseEntity> item)
    {
        this.SetParent(item.Value);
        this._entityCollection.Add(item.Key, item.Value);
    }

    /// <summary>
    /// Procédure qui retire tout les éléments de la collection
    /// </summary>
    /// <remarks></remarks>
    public void Clear()
    {
        this.EntityCollection.Clear();
    }

    /// <summary>
    /// Fonction qui indique si la collection contient l'élément
    /// </summary>
    /// <param name="item">Item à rechercher</param>
    /// <returns>Boolean</returns>
    /// <remarks></remarks>
    public bool Contains(System.Collections.Generic.KeyValuePair<object, IBaseEntity> item)
    {
        return this.EntityCollection.ContainsKey(item.Key);
    }

    public void CopyTo(System.Collections.Generic.KeyValuePair<object, IBaseEntity>[] array, int arrayIndex)
    {
        //TODO: faire le copy
    }

    public int Count
    {
        get { return this._entityCollection.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    /// <summary>
    /// Fonction qui permet de retirer un élément de la collection
    /// </summary>
    /// <param name="item">Item à retirer</param>
    /// <returns>Boolean</returns>
    /// <remarks></remarks>
    public bool Remove(System.Collections.Generic.KeyValuePair<object, IBaseEntity> item)
    {
        return this.EntityCollection.Remove(item.Key);
    }

    /// <summary>
    /// Procédure d'ajout d'un élément dans la collection
    /// </summary>
    /// <param name="key">Clé</param>
    /// <param name="value">Valeur</param>
    /// <remarks></remarks>
    public void AddEx(object key, IBaseEntity value)
    {
        this.SetParent(value);
        this.EntityCollection.Add(key, value);
    }
    void System.Collections.Generic.IDictionary<object, IBaseEntity>.Add(object key, IBaseEntity value)
    {
        AddEx(key, value);
    }

    /// <summary>
    /// Fonction qui indique si la clé existe dans la collection
    /// </summary>
    /// <param name="key">Clé</param>
    /// <returns>Boolean</returns>
    /// <remarks></remarks>
    public bool ContainsKey(object key)
    {
        return this.EntityCollection.ContainsKey(key);
    }

    public IBaseEntity this[object key]
    {
        get { return this.EntityCollection[key]; }
        set { this.EntityCollection[key] = value; }
    }

    public System.Collections.Generic.ICollection<object> Keys
    {
        get { return this.EntityCollection.Keys; }
    }

    /// <summary>
    /// Fonction qui permet de retirer un élément de la collection
    /// </summary>
    /// <param name="key">Clé</param>
    /// <returns>Boolean</returns>
    /// <remarks></remarks>
    public bool RemoveEx(object key)
    {
        return this.EntityCollection.Remove(key);
    }
    bool System.Collections.Generic.IDictionary<object, IBaseEntity>.Remove(object key)
    {
        return RemoveEx(key);
    }

    /// <summary>
    /// Fonction qui tente de lire une valeur dans la collection
    /// </summary>
    /// <param name="key">Clé</param>
    /// <param name="value">Valeur</param>
    /// <returns>Boolean</returns>
    /// <remarks></remarks>
    public bool TryGetValue(object key, out IBaseEntity value)
    {
        return this.EntityCollection.TryGetValue(key, out value);
    }

    public System.Collections.Generic.ICollection<IBaseEntity> Values
    {
        get { return this.EntityCollection.Values; }
    }

    /// <summary>
    /// Fonction qui permet de retourner un Énumérateur de la collection
    /// </summary>
    /// <returns>IEnumerator</returns>
    /// <remarks></remarks>
    public System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<object, IBaseEntity>> GetEnumerator()
    {
        return this.EntityCollection.GetEnumerator();
    }

    /// <summary>
    /// Fonction qui permet de retourner un Énumérateur de la collection
    /// </summary>
    /// <returns>IEnumerator</returns>
    /// <remarks></remarks>
    public System.Collections.IEnumerator GetEnumeratorEx()
    {
        return this.EntityCollection.GetEnumerator();
    }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumeratorEx();
    }

    /// <summary>
    /// Fonction qui copy la collection dans un array list
    /// </summary>
    /// <returns>List(Of IBaseEntity)</returns>
    /// <remarks></remarks>
    public System.Collections.Generic.List<IBaseEntity> ToArrayList()
    {
        return this._entityCollection.ToArrayList();
    }

    public string SortOrder
    {
        get { return this._entityCollection.SortOrder; }
        set { this._entityCollection.SortOrder = value; }
    }

    #endregion

    #region "IContainer"

    public System.Type EntityType
    {
        get { return this._entitiesType; }
    }

    public IEntityCollection<object, IBaseEntity> EntityCollection
    {
        get { return this._entityCollection; }
    }

    public bool IsLoaded
    {
        get { return this._isLoaded; }
        set { this._isLoaded = value; }
    }

    /// <summary>
    /// Fonction qui permet de créer un entité
    /// </summary>
    /// <returns>IBaseEntity</returns>
    /// <remarks></remarks>
    public IBaseEntity CreateEntity(IDbConnection connection = null)
    {
        IBaseEntity entity = default(IBaseEntity);
        entity = (IBaseEntity)Activator.CreateInstance(this._entitiesType, new object[] {
			true,
			connection
		});
        SetParent(entity);
        this.EntityCollection.Add(entity.GetPrimaryKeyField().Value, entity);
        return entity;
    }

    /// <summary>
    /// Procédure qui permet de dupliquer un entité
    /// </summary>
    /// <param name="entity">L'entité à dupliquer</param>
    /// <returns>IBaseEntity</returns>
    /// <remarks></remarks>
    public IBaseEntity DuplicateEntity(IBaseEntity entity, IDbConnection connection = null )
    {
        IBaseEntity newEntity = (IBaseEntity)entity.Duplicate(true, connection);
        SetParent(newEntity);
        this.EntityCollection.Add(newEntity.GetPrimaryKeyField().Value, newEntity);
        return newEntity;
    }

    /// <summary>
    /// Fonction qui retire un entité de la collection
    /// </summary>
    /// <param name="entity">L'entité à retirer</param>
    /// <returns>Boolean</returns>
    /// <remarks></remarks>
    public bool RemoveEntity(IBaseEntity entity)
    {
        return this.EntityCollection.Remove(entity.GetPrimaryKeyField().Value);
    }

    /// <summary>
    /// Procédure de chargement des données
    /// </summary>
    /// <remarks></remarks>
    public void LoadData(IDbConnection connection = null, Int32 fetchRowCount = 250, Int32 rowsPerPage = 0, Int32 pageIndex = 0)
    {
        this.LoadData(false, connection, fetchRowCount, rowsPerPage, pageIndex);
    }

    /// <summary>
    /// Procédure qui charge les données
    /// </summary>
    /// <param name="forceReload">Indique si on doit forcer le reload</param>
    /// <remarks></remarks>
    public void LoadData(bool forceReload, IDbConnection connection = null, Int32 fetchRowCount = 250, Int32 rowsPerPage = 0, Int32 pageIndex = 0)
    {
        if ((!this._isLoaded) | (forceReload))
        {
            //Set le filtre sur le parent
            List<string> filterList = new List<string>();
            filterList.AddRange(this._filters);
            IBaseEntity childEntity = (IBaseEntity)Activator.CreateInstance(this._entitiesType, new object[] { connection });
            filterList.Add(" AND " + ((IEntityField)childEntity.GetPrimaryKeyField()).TableName + "." + this._fieldNameToLink + " = " + this.Parent.GetPrimaryKeyField().OriginalValue.ToString());
            //Set le trie
            string sortOrder = null;
            if ((this._entityCollection.SortOrder == null) || (this._entityCollection.SortOrder == string.Empty))
            {
                sortOrder = childEntity.GetDefaultSortOrder();
            }
            else
            {
                sortOrder = this._entityCollection.SortOrder;
            }
            this._entityCollection = (IEntityCollection<object,IBaseEntity>)BaseEntity.GetEntityList(this._entitiesType, filterList, sortOrder, connection, fetchRowCount, rowsPerPage, pageIndex);
        }
        this._isLoaded = true;
    }

    /// <summary>
    /// Fonction qui permet de sauvegarder le container
    /// </summary>
    /// <returns>Boolean</returns>
    /// <remarks></remarks>
    public bool Save(IDbConnection connection = null)
    {
        return true;
    }

    /// <summary>
    /// Fonction qui permet de valider un entité
    /// </summary>
    /// <returns>List(Of AppException)</returns>
    /// <remarks></remarks>
    public virtual List<AppException> ValidateRules()
    {
        return new List<AppException>();
    }

    /// <summary>
    /// Fonction qui permet de savoir s'il y a eu des changements à la liste
    /// </summary>
    /// <returns>Boolean</returns>
    /// <remarks></remarks>
    public virtual bool IsDirty()
    {
        foreach (IBaseEntity entity in this._entityCollection.Values)
        {
            if (entity.IsDirty())
            {
                return true;
            }
        }
        return false;
    }

    /// <summary>
    /// Procédure qui applique les modifications aux entités
    /// </summary>
    /// <remarks></remarks>
    public virtual void AcceptChanges()
    {
        List<IBaseEntity> entitiesToRemove = new List<IBaseEntity>();
        foreach (IBaseEntity entity in this._entityCollection.Values)
        {
            if (entity.MarkAsDeleted)
            {
                entitiesToRemove.Add(entity);
            }
            else
            {
                entity.AcceptChanges();
            }
        }
        foreach (IBaseEntity entity in entitiesToRemove)
        {
            this.RemoveEntity(entity);
        }
    }

    /// <summary>
    /// Procédure qui rejette les modifications aux entités
    /// </summary>
    /// <remarks></remarks>
    public virtual void RejectChanges()
    {
        List<IBaseEntity> entitiesToRemove = new List<IBaseEntity>();
        foreach (IBaseEntity entity in this._entityCollection.Values)
        {
            if (entity.IsNew)
            {
                entitiesToRemove.Add(entity);
            }
            else
            {
                entity.RejectChanges();
            }
        }
        foreach (IBaseEntity entity in entitiesToRemove)
        {
            this.RemoveEntity(entity);
        }
    }

    #endregion

    #region "Événements"
    public event EntityAddedEventHandler EntityAdded;
    public delegate void EntityAddedEventHandler(object sender, IBaseEntity e);
    public event EntityRemovedEventHandler EntityRemoved;
    public delegate void EntityRemovedEventHandler(object sender, IBaseEntity e);
    #endregion


}
